package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-resty/resty/v2"
	"gopkg.in/ini.v1"
	"io"
	"log"
	"os"
	"sync"
	"time"
)

// 错误处理函数
func checkErr(err error) {
	if err != nil {
		log.Println(err)
		panic(err)
	}
}

type AppConfig struct {
	SourceApisixUrl      string `ini:"source.apisix.url"`
	SourceApisixUsername string `ini:"source.apisix.username"`
	SourceApisixPassword string `ini:"source.apisix.password"`
	DestApisixUrl        string `ini:"dest.apisix.url"`
	DestApisixUsername   string `ini:"dest.apisix.username"`
	DestApisixPassword   string `ini:"dest.apisix.password"`
}

func main() {
	//记录日志
	f, err := os.OpenFile("main.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
	if err != nil {
		return
	}
	defer func() {
		f.Close()
	}()

	// 组合一下即可，os.Stdout代表标准输出流
	multiWriter := io.MultiWriter(os.Stdout, f)
	log.SetOutput(multiWriter)
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	config, err := ini.Load("config.ini")
	checkErr(err)
	//读取并映射到结构体中
	var appConfig = new(AppConfig)
	err = config.MapTo(appConfig)
	log.Printf("配置信息:%s", appConfig)
	if err != nil {
		fmt.Printf("映射配置信息失败 %v", err)
		return
	}

	currentTime := time.Now()
	dirName := currentTime.Format("20060102150405")

	client, accessToken, err, done := login(appConfig.SourceApisixUrl, appConfig.SourceApisixUsername, appConfig.SourceApisixPassword, err)
	if done {
		return
	}

	err, allRoutesResp, done2 := getAllRoutes(appConfig.SourceApisixUrl, accessToken, err, client)
	if done2 {
		return
	}

	log.Printf("getAllRoutes的总数:%f", allRoutesResp["data"].(map[string]interface{})["total_size"].(float64))

	routeRows := allRoutesResp["data"].(map[string]interface{})["rows"]

	err = os.Mkdir(dirName, os.ModeDir)
	if err != nil {
		log.Println(err)
		return
	}

	for _, item := range routeRows.([]interface{}) {
		itemMap := item.(map[string]interface{})
		//delete(itemMap, "id")
		delete(itemMap, "create_time")
		delete(itemMap, "update_time")
		content, _ := json.Marshal(itemMap)
		withFile, err := os.Create(dirName + "./" + itemMap["name"].(string))
		if err != nil {
			log.Println(err)
			continue
		}
		defer withFile.Close()

		withFile.Write([]byte(content))
	}

	log.Println("导出完成")

	destClient, destAccessToken, err, done := login(appConfig.DestApisixUrl, appConfig.DestApisixUsername, appConfig.DestApisixPassword, err)

	var wg sync.WaitGroup

	for _, destItem := range routeRows.([]interface{}) {
		itemMap := destItem.(map[string]interface{})
		fileName := dirName + "/" + itemMap["name"].(string)
		content, _ := os.ReadFile(fileName)
		//log.Printf("读取文件:%s", fileName)

		var contentJson map[string]interface{}
		err = json.Unmarshal(content, &contentJson)
		if err != nil {
			log.Println(err)
			return
		}

		if itemMap["upstream_id"] != nil {
			err, updateStreamResp, done4 := getUpstreams(appConfig.SourceApisixUrl, accessToken, itemMap["upstream_id"].(string), err, client)
			if done4 {
				return
			}
			updateStreamReq := updateStreamResp["data"].(map[string]interface{})
			delete(updateStreamReq, "create_time")
			delete(updateStreamReq, "update_time")
			upstreamId := itemMap["upstream_id"].(string)
			err, _, done4 = createUpstream(appConfig.DestApisixUrl, destAccessToken, upstreamId, updateStreamReq, err, destClient)
			if done4 {
				return
			}
			//contentJson["upstream_id"] = createStreamResp["data"].(map[string]interface{})["id"]
		}

		routeId := itemMap["id"].(string)

		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			createRoute(appConfig.DestApisixUrl, destAccessToken, routeId, contentJson, err, destClient)
		}(1)
	}
	wg.Wait()
	log.Println("执行完毕，退出")

}

func getUpstreams(url string, accessToken string, upstreamId string, err error, client *resty.Client) (error, map[string]interface{}, bool) {
	namespacesPath := fmt.Sprintf("%s/apisix/admin/upstreams/%s", url, upstreamId)
	response, err := client.R().
		SetHeaders(map[string]string{"Authorization": accessToken}).
		Get(namespacesPath)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}

	var routesResp map[string]interface{}
	err = json.Unmarshal(response.Body(), &routesResp)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}
	if routesResp["code"].(float64) != 0 {
		log.Println("调用getUpstreams失败:%s", routesResp)
		return nil, nil, true
	}
	return err, routesResp, false
}

func getAllRoutes(url string, accessToken string, err error, client *resty.Client) (error, map[string]interface{}, bool) {
	namespacesPath := fmt.Sprintf("%s/apisix/admin/routes?label=&page=1&page_size=500&host=&desc=&id=", url)
	response, err := client.R().
		SetHeaders(map[string]string{"Authorization": accessToken}).
		Get(namespacesPath)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}

	var routesResp map[string]interface{}
	err = json.Unmarshal(response.Body(), &routesResp)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}
	return err, routesResp, false
}

func login(url string, username string, password string, err error) (*resty.Client, string, error, bool) {
	loginPath := fmt.Sprintf("%s/apisix/admin/user/login", url)
	data := map[string]string{"username": username, "password": password}

	headers := map[string]string{"Content-Type": "application/json;charset=UTF-8"}
	// Create a Resty client
	client := resty.New()
	response, err := client.R().
		SetBody(data).
		SetHeaders(headers).
		Post(loginPath)
	if err != nil {
		log.Println(err)
		return nil, "", nil, true
	}
	if response.RawResponse.StatusCode != 200 {
		log.Printf("连接apisix失败,连接地址：%s,失败原因：%s", loginPath, response.RawResponse.Status)
		return nil, "", nil, true
	}

	var results map[string]interface{}
	err = json.Unmarshal(response.Body(), &results)
	if err != nil {
		log.Printf("调用源apisix失败%s", err)
		return nil, "", nil, true
	}

	log.Println(results)
	if results["code"].(float64) != 0 {
		log.Printf("调用apisix失败%s", err)
		return nil, "", nil, true
	}

	token := results["data"].(map[string]interface{})["token"].(string)
	return client, token, err, false
}

func createRoute(url string, accessToken string, routeId string, content map[string]interface{}, err error, client *resty.Client) (error, map[string]interface{}, bool) {
	namespacesPath := fmt.Sprintf("%s/apisix/admin/routes/%s", url, routeId)
	response, err := client.R().
		SetHeaders(map[string]string{"Authorization": accessToken}).
		SetBody(content).
		Put(namespacesPath)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}

	var routesResp map[string]interface{}
	err = json.Unmarshal(response.Body(), &routesResp)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}
	if routesResp["code"].(float64) != 0 {
		log.Println("调用createRoute失败:", routesResp)
		log.Println("调用createRoute失败,内容是::", content)
		return nil, nil, true
	}
	return err, routesResp, false
}

func createUpstream(url string, accessToken string, upstreamId string, content map[string]interface{}, err error, client *resty.Client) (error, map[string]interface{}, bool) {
	namespacesPath := fmt.Sprintf("%s/apisix/admin/upstreams/%s", url, upstreamId)
	response, err := client.R().
		SetHeaders(map[string]string{"Authorization": accessToken}).
		SetBody(content).
		Put(namespacesPath)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}

	var routesResp map[string]interface{}
	err = json.Unmarshal(response.Body(), &routesResp)
	if err != nil {
		log.Println(err)
		return nil, nil, true
	}
	if routesResp["code"].(float64) != 0 {
		log.Println("调用createUpstream失败:%s", routesResp)
		return nil, nil, true
	}
	return err, routesResp, false
}
